/*
 * Copyright (c) 2003-2004, Inversoft, All Rights Reserved
 *
 * This software is distribuable under the GNU Lesser General Public License.
 * For more information visit gnu.org.
 */
package org.emergent.antbite.savant;


import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * <p>
 * This is used to iterate over depedency lists (artifacts
 * and artifact groups) and call out to interested listeners.
 * </p>
 *
 * @author Brian Pontarelli
 */
public class DependencyMediator {

    private static Set projectsBuilt = new HashSet();
    private static Map artifactsFetched = new HashMap();

    private List listeners = new ArrayList();
    private Dependencies dependencies = new Dependencies();
    private LocalCacheStore localCacheStore;
    private Workflow workflow;


    /**
     * Creates a new DepedencyMediator.
     */
    public DependencyMediator() {
    }


    /**
     * Fetches the local cache location from this mediators {@link LocalCacheStore}.
     * If the local cache hasn't been constructed, this returns null.
     */
    public File getLocalCacheDir() {
        return (localCacheStore == null) ? null : localCacheStore.getLocation();
    }

    /**
     * Sets the local cache location for this mediators {@link LocalCacheStore}.
     *
     * @param   localCacheDir The directory that is used as the LocalCacheStore
     *          location
     * @throws  SavantException If the File given is invalid
     */
    public void setLocalCacheDir(File localCacheDir) throws SavantException {
        this.localCacheStore = new LocalCacheStore(localCacheDir);
    }

    /**
     * Adds the given listener from the list of lisetners.
     *
     * @param   listener The listener to add
     */
    public void addListener(DependencyListener listener) {
        listeners.add(listener);
    }

    /**
     * Removes the given listener from the list of lisetners.
     *
     * @param   listener The listener to remove
     */
    public void removeListener(DependencyListener listener) {
        listeners.remove(listener);
    }

    /**
     * Fetches the dependencies that will be mediated over during mediation.
     *
     * @return  The {@link Dependencies} object or null if one was never set
     */
    public Dependencies getDependencies() {
        return dependencies;
    }

    /**
     * Sets the dependencies that will be mediated over during mediation.
     *
     * @param   dependencies The new dependencies to mediate over
     */
    public void setDependencies(Dependencies dependencies) {
        this.dependencies = dependencies;
    }

    /**
     * Retrieves the workflow.
     */
    public Workflow getWorkflow() {
        return workflow;
    }

    /**
     * Sets the workflow.
     */
    public void setWorkflow(Workflow workflow) {
        this.workflow = workflow;
    }

    /**
     * Executes this task that builds up the dependencies and such.
     */
    public void mediate() throws SavantException {
        workflow.validate();

        handleGroups(dependencies.getArtifactGroups());

        Iterator aIter = dependencies.getArtifacts().iterator();
        while (aIter.hasNext()) {
            Artifact artifact = (Artifact) aIter.next();
            handleArtifact(artifact, null);
        }
    }

    /**
     * Handles iteration over ArtifactGroups. This calls the
     * {@link #handleArtifact(Artifact, ArtifactGroup)} for each artifact in the
     * group and then recurses for each ArtifactGroup in the group.
     *
     * @param   groups The groups to retrieve the artifacts and sub-groups from
     * @throws  SavantException If the mediation failed for any reason
     */
    protected void handleGroups(List groups) throws SavantException {
        // Loop over all the artifact group and try to fetch the artifacts
        for (int i = 0; i < groups.size(); i++) {
            ArtifactGroup group = (ArtifactGroup) groups.get(i);
            List artifacts = group.getArtifacts();
            for (int j = 0; j < artifacts.size(); j++) {
                Artifact artifact = (Artifact) artifacts.get(j);
                handleArtifact(artifact, group);
            }

            List subGroups = group.getArtifactGroups();
            if (subGroups.size() > 0) {
                handleGroups(subGroups);
            }
        }
    }

    /**
     * Handles the fetching of a single artifact. This method also optionally takes
     * a file set and path (classpath) that the artifact is added to. If these
     * are null, they are ignored.
     *
     * @param   artifact The artifact to fetch and store
     */
    protected void handleArtifact(Artifact artifact, ArtifactGroup group)
    throws SavantException {
        // Setup the dependencies (if any)
        workflow.resolveArtifactDependencies(artifact, localCacheStore);

        // Go find the file and build the projects
        File file = (File) artifactsFetched.get(artifact);
        if (file == null) {
            String artifactProject = artifact.getProjectname();
            String artifactGroup = artifact.getGroup();

            // Figure out if this project is local and if so, build it
            String projectID = LocalProject.makeProjectID(artifactGroup,
                artifactProject);
            LocalProject project = dependencies.getProject(projectID);
            if (project != null && !projectsBuilt.contains(project)) {
                project.build();
                projectsBuilt.add(project);

                for (int i = 0; i < listeners.size(); i++) {
                    DependencyListener listener = (DependencyListener) listeners.get(i);
                    listener.projectBuilt(project);
                }
            }

            // Let the workflow handle the resolution from both the local cache and
            // the process objects.
            file = workflow.findArtifact(artifact, localCacheStore);
            if (file == null) {
                throw new SavantException("Unable to locate dependency [" +
                    artifact.toString() + "]");
            }

            artifactsFetched.put(artifact, file);
        }

        for (int i = 0; i < listeners.size(); i++) {
            DependencyListener listener = (DependencyListener) listeners.get(i);
            listener.artifactFound(file, artifact, group);
        }

        // Recurse to handle the dependencies of the artifact
        List deps = artifact.getDependencies();
        if (deps.size() > 0) {
            for (int i = 0; i < deps.size(); i++) {
                handleArtifact((Artifact) deps.get(i), group);
            }
        }
    }
}